namespace jQube.Web.UI
{
    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Security.Permissions;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using jQube.Resources;
    using jQube.Web.UI.Design;
    using System.Diagnostics.CodeAnalysis;
    using System.Security;
    using System.Reflection;
    using System.Collections.Generic;
    using jQube.Web.UI.Controls;

    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [Designer(typeof(ScriptManagerDesigner))]
    [NonVisualControl(true)]
    [ParseChildren(true)]
    [PersistChildren(false)]
    [ToolboxBitmap(typeof(AssemblyResourceFinder), "jQube.Resources.ScriptManager.bmp")]
    public class ScriptManager : WebControl
    {
        internal const string IsAsyncPostBackHiddenField = "__ISASYNCPOSTBACK";
        internal const string UpdatePanelsToUpgradeHiddenField = "__UPDATEPANELSTOUPGRADE";

        private bool debug;
        private bool enablePartialRendering;
        private bool supportsPartialRendering;
        private bool supportsPartialRenderingSetByUser;

        private bool initCompleted;

        private IPage page;
        private PageRequestManager pageRequestManager;
        private ScriptsRegistrationManager scriptsRegistrationManager;
        private event AsyncPostBackErrorEventHandler asyncPostBackError = null;
        private ICustomErrorsSection customErrorSection;
        private List<UpdatePanel> updatePanels;
        private ScriptReferenceCollection scriptReferences;

        private static readonly object reflectionLock = new object();
        private static PropertyInfo enableLegacyRenderingPropertyInfo;

        public ScriptManager()
            : base(HtmlTextWriterTag.Div)
        {
            this.Debug = true;
            this.enablePartialRendering = true;
            this.supportsPartialRendering = true;
            this.supportsPartialRenderingSetByUser = false;
            this.initCompleted = false;
        }

        public event AsyncPostBackErrorEventHandler AsyncPostBackError
        {
            add { this.asyncPostBackError += value; }
            remove { this.asyncPostBackError -= value; }
        }

        public bool AllowCustomErrorsRedirect { get; set; }

        internal ICustomErrorsSection CustomErrorsSection
        {
            [SecurityCritical()]
            get
            {
                if (this.customErrorSection == null)
                {
                    this.customErrorSection = new CustomErrorsSectionWrapper();

                }

                return this.customErrorSection;
            }
        }

        [Category("Scripting")]
        [DefaultValue(true)]
        public bool Debug
        {
            get
            {
                return this.debug;
            }
            set
            {
                this.debug = value;
            }
        }

        [Category("Behavior")]
        [DefaultValue(true)]
        public bool EnablePartialRendering
        {
            get
            {
                return this.enablePartialRendering;
            }
            set
            {
                if (this.initCompleted)
                {
                    throw new InvalidOperationException(AtlasWeb.ScriptManager_CannotChangeEnablePartialRendering);
                }

                this.enablePartialRendering = value;
            }
        }

        [Browsable(false)]
        [DefaultValue(true)]
        [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
        public bool SupportsPartialRendering
        {
            get
            {
                if (!this.EnablePartialRendering)
                {
                    return false;
                }
                return this.supportsPartialRendering;
            }
            set
            {
                if (!this.EnablePartialRendering)
                {
                    throw new InvalidOperationException(AtlasWeb.ScriptManager_CannotSetSupportsPartialRenderingWhenDisabled);
                }
                if (this.initCompleted)
                {
                    throw new InvalidOperationException(AtlasWeb.ScriptManager_CannotChangeSupportsPartialRendering);
                }

                this.supportsPartialRendering = value;
                this.supportsPartialRenderingSetByUser = true;
            }
        }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ScriptReferenceCollection ScriptReferences
        {
            get
            {
                if (this.scriptReferences == null)
                {
                    this.scriptReferences = new ScriptReferenceCollection(this);
                }

                return scriptReferences;
            }
        }

        [Browsable(false)]
        public bool IsAsyncPostBack
        {
            get
            {
                bool isAsyncPostBack = false;

                if (this.Page.IsPostBack &&
                    !string.IsNullOrWhiteSpace(this.Page.Request[IsAsyncPostBackHiddenField]))
                {
                    isAsyncPostBack = Convert.ToBoolean(this.Page.Request[IsAsyncPostBackHiddenField]);
                }

                return isAsyncPostBack;
            }
        }

        [Category("Behavior")]
        [DefaultValue(true)]
        public bool ShowActivityIndicator { get; set; }

        internal bool SupportsPartialRenderingSetByUser
        {
            get { return this.supportsPartialRenderingSetByUser; }
        }

        internal PageRequestManager PageRequestManager
        {
            get
            {
                if (this.pageRequestManager == null)
                {
                    this.pageRequestManager = new PageRequestManager(this);
                }

                return this.pageRequestManager;
            }
        }

        [ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
        [SecurityCritical()]
        [SecuritySafeCritical()]
        internal bool GetEnableLegacyRendering()
        {
            if (ScriptManager.enableLegacyRenderingPropertyInfo == null)
            {
                lock (ScriptManager.reflectionLock)
                {
                    if (ScriptManager.enableLegacyRenderingPropertyInfo == null)
                    {
                        ScriptManager.enableLegacyRenderingPropertyInfo = typeof(ScriptManager).GetProperty("EnableLegacyRendering", BindingFlags.NonPublic | BindingFlags.Instance);
                    }
                }
            }
            return (bool)ScriptManager.enableLegacyRenderingPropertyInfo.GetValue(this, null);
        }

        internal ScriptsRegistrationManager ScriptRegistrationManager
        {
            get
            {
                if (this.scriptsRegistrationManager == null)
                {
                    this.scriptsRegistrationManager = new ScriptsRegistrationManager(this);
                }

                return this.scriptsRegistrationManager;
            }
        }

        internal IPage IPage
        {
            get
            {
                if (this.page == null)
                {
                    this.page = new PageWrapper(this.Page);
                }

                return this.page;
            }
        }

        internal List<UpdatePanel> UpdatePanels
        {
            get
            {
                if (this.updatePanels == null)
                {
                    this.updatePanels = new List<UpdatePanel>();
                }

                return this.updatePanels;
            }
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (!this.DesignMode)
            {
                ScriptManager currentScriptManager = ScriptManager.GetCurrent(base.Page);

                if (currentScriptManager != null)
                    throw new InvalidOperationException(AtlasWeb.ScriptManager_OnlyOneScriptManager);

                this.IPage.Items[typeof(ScriptManager)] = this;

                this.IPage.InitComplete += new EventHandler(this.PageRequestManager.OnPageInitComplete);
                this.IPage.PreRender += new EventHandler(this.PageRequestManager.OnPagePreRender);
                this.IPage.PreRenderComplete += new EventHandler(this.PageRequestManager.OnPagePreRenderComplete);

                if (this.IsAsyncPostBack)
                {
                    //this.IPage.EnableEventValidation = false;
                    this.IPage.Error += new EventHandler(this.PageRequestManager.OnPageError);
                }
            }
        }

        public static ScriptManager GetCurrent()
        {
            HttpContext context = HttpContext.Current;
            return ScriptManager.GetCurrent(context.Handler as Page);
        }

        public static ScriptManager GetCurrent(Page page)
        {
            if (page == null)
                throw new ArgumentNullException("page");

            return page.Items[typeof(ScriptManager)] as ScriptManager;
        }

        internal void AddRequiredScript(Type type)
        {
            this.PageRequestManager.AddRequiredScript(type);
        }

        internal void AddRequiredStyleSheet(Type type)
        {
            this.PageRequestManager.AddRequiredStyleSheet(type);
        }

        internal void AddInitilizationScript(string script)
        {
            this.PageRequestManager.AddInitilizationScript(script);
        }

        internal void OnAsyncPostBackError(AsyncPostBackErrorEventArgs e)
        {
            if (this.asyncPostBackError != null)
            {
                this.asyncPostBackError.Invoke(this, e);
            }
        }
    }
}
